# Copyright (c) 2025, qiushike and contributors
# For license information, please see license.txt

import frappe
from frappe import _
from frappe.model.document import Document
from frappe.query_builder import Order


class JobCardScanInput(Document):
	@frappe.whitelist()
	def get_tracking_number(self):
		"""
		根据扫码或手输的至少最后5位追踪号获取追踪号信息
		"""

		def get_next_operation_message(tracking_number):
			"""
			返回下个工序的出错消息，正常就不返回出错消息
			"""
			operations = self.get_tracking_number_next_operations(tracking_number)
			if not operations:
				return _("Tracking number not ready for this operation")
			elif self.operation and operations[0] != self.operation:
				return _("Operation {0} mismatch").format(operations[0])
			elif not self.operation:
				self.operation = operations[0]
		
		dt_tracking_number = frappe.qb.DocType("Tracking Number")
		dt_work_order = frappe.qb.DocType("Work Order")
		
		query = frappe.qb.from_(dt_tracking_number
		).join(dt_work_order
		).on(dt_tracking_number.work_order == dt_work_order.name
		).select(
			dt_tracking_number.name,
			dt_tracking_number.work_order,
			dt_tracking_number.is_parent_tracking_number,
			dt_work_order.status
		)
		
		valid_work_order_status = ["Submitted","Not Started","In Process"]

		tracking_number = self.tracking_number
		input_length = len(tracking_number)
		if input_length < 5:
			return _("Input tracking number length need to be at least 6")
		elif input_length < 10:
			# 手工输入追踪号尾号
			query = query.where(dt_tracking_number.tracking_number.like(f"%{tracking_number}"))
		else:
			query = query.where(dt_tracking_number.tracking_number==tracking_number)

		tracking_data = query.run(as_list=1)
		if not tracking_data:
			return (_("Tracking number {0} not exist").format(tracking_number))
		else:
			(tracking_number, work_order, is_parent_tracking_number, work_order_status) = tracking_data[0]
			self.tracking_number = tracking_number
			if work_order_status not in valid_work_order_status:
				return (_("Tracking number {0} work order {1} in non active status {2}").format(
					tracking_number, work_order, work_order_status))
		if not self.work_order:					
			self.work_order = work_order
		# 设置一个可复用的工单单据			
		self.set_work_order_doc()
		# 追踪号不属于当前工单，则返回工单号	
		if self.work_order != work_order:
			return  _("Wrong Work order {0}").format(work_order)
		# 检查是否属于当前工序
		existing_operation = self.operation
		if is_parent_tracking_number:
			child_tracking_numbers = frappe.get_all("Tracking Number", 
				{
					"parent_tracking_number": tracking_number
				},
				pluck="name"
			)
			for (i, child_tracking_number) in enumerate(child_tracking_numbers):
				# 子追踪号属于同一个工单，同一个主追踪号，工序进度状态一样，获取下个工序只要执行一次
				if not i:
					msg = get_next_operation_message(child_tracking_number)
					if msg:
						return msg
				if self.enable_tracking_detail:		
					self.append('tracking_details', {
						"tracking_number": child_tracking_number,
						"parent_tracking_number": tracking_number
					})	
		else:			
			msg = get_next_operation_message(tracking_number)
			if msg:
				return msg
			if self.enable_tracking_detail:
				self.append('tracking_details', {
					"tracking_number": tracking_number
				})
		self.set_completed_qty()

	def set_completed_qty(self):
		if self.tracking_details and self.conversion_factor:
			self.tracking_qty = len(self.tracking_details)
			self.completed_qty = self.tracking_qty * self.conversion_factor

	def set_work_order_doc(self):
		if not getattr(self, "work_order_doc", None):
			self.work_order_doc = frappe._dict()

	@frappe.whitelist()
	def get_work_order_next_operations(self):
		"""
		返回当前工单下一个或多个待完成工序
		"""
		if self.work_order:
			work_order_doc = frappe.get_doc("Work Order", self.work_order)
			self.conversion_factor = work_order_doc.get("meter_per_roll") or self.conversion_factor
			operations = [] 
			sequence_id = None
			for row in work_order_doc.operations:
				if not sequence_id:
					if (row.completed_qty + row.process_loss_qty) < work_order_doc.qty:
						operations.append(row.operation)
						if not self.operation:
							self.operation = row.operation
						elif self.operation != row.operation:
							work_order = self.work_order
							self.work_order=""
							frappe.throw(_(f"所选工单 {work_order} 当前工序 {row.operation} 与用户工序 {self.operation} 不匹配"))
							return
						operation_data = frappe.db.get_value("Operation", row.operation, 
							["dynamic_fields", "tracking_type"])
						if operation_data:
							(self.dynamic_fields, self.tracking_type) = operation_data
						job_card_data = frappe.db.get_value("Job Card", {
							"operation_id": row.name,
							"docstatus": 0
						}, ["name", "for_quantity"])
						if job_card_data:
							(self.job_card, self.for_quantity) = job_card_data					
						sequence_id = row.sequence_id
				else:
					if row.sequence_id == sequence_id:
						operations.append(row.operation)
					elif row.sequence_id > sequence_id:
						break
			self.set_completed_qty()

			return operations

	@frappe.whitelist()
	def get_tracking_number_next_operations(self, tracking_number):
		"""
		返回追踪号子表中的追踪号下一个或多个待完成工序
		"""
		jc = frappe.qb.DocType("Job Card")
		jd = frappe.qb.DocType("Tracking Detail")
		if self.work_order:
			# 本追踪号上一个已报工工序顺序号						
			last_operation = frappe.qb.from_(jc
			).left_join(jd
			).on(jc.name == jd.parent
			).select(jc.operation, jc.sequence_id, jc.operation_id
			).where(
				(
					(jc.tracking_number == tracking_number) | 
					(jd.tracking_number == tracking_number)
				) & (jc.work_order == self.work_order) &
				(jc.docstatus < 2)
			).orderby(jc.sequence_id, order=Order.desc
			).limit(1
			).run(as_dict = 1)
			if last_operation:
				last_sequence_id = last_operation[0].sequence_id
				last_operation_id = last_operation[0].operation_id
			else:
				last_sequence_id = 0
				last_operation_id = ""

			if not self.work_order_doc:
				self.work_order_doc = frappe.get_doc("Work Order", self.work_order)

			self.conversion_factor = self.work_order_doc.get("meter_per_roll") or self.conversion_factor
			operations = [] 
			
			# 获取上个工序本追踪号可用于本工序报工的总良品数
			if last_operation and not self.enable_tracking_detail:
				last_completed_qty = frappe.db.get_value("Job Card", 
					{
						"sequence_id": last_operation[0].sequence_id,
						"operation": last_operation[0].operation,
						"tracking_number": tracking_number,
						"work_order": self.work_order,
						"docstatus": ("<", 2)
					},
					"sum(total_completed_qty)"
				)
			else:
				last_completed_qty = 0

			authorized_operations = frappe.get_list("Operation", 
				filters = {"name": ("in", [r.operation for r in self.work_order_doc.operations])},
				pluck="name"
			)
			# 是否要按sequence_id 先排序？
			next_valid_sequence_id = None
			next_sequence_id = 0
			for row in self.work_order_doc.operations:
				if row.sequence_id and row.sequence_id > last_sequence_id:
					next_sequence_id = row.sequence_id					
					break

			for row in self.work_order_doc.operations:				
				if not next_valid_sequence_id:
					if (
						row.name != last_operation_id and
						row.sequence_id and row.sequence_id >= last_sequence_id and 
						row.sequence_id <= next_sequence_id and
						row.operation in authorized_operations
					):
						operations.append(row.operation)
						next_valid_sequence_id = row.sequence_id
						operation_data = frappe.db.get_value("Operation", row.operation, 
							["name as opertion", "dynamic_fields", "tracking_type", "enable_tracking_detail"],
							as_dict=1
						)
						self.update(operation_data)
						job_card_data = frappe.db.get_value("Job Card", {
							"operation_id": row.name,
							"docstatus": 0
						},
						["name as job_card", "for_quantity", "total_completed_qty"], as_dict = 1)
						self.update(job_card_data)
						if last_completed_qty:
							self.for_quantity = last_completed_qty
							self.total_completed_qty = last_completed_qty										
				else:
					if row.sequence_id == next_valid_sequence_id:
						operations.append(row.operation)
					elif row.sequence_id > next_valid_sequence_id:
						break
			return operations

	@frappe.whitelist()
	def update_job_card(self):
		job_card_doc = frappe.get_doc("Job Card", self.job_card)
		if self.workstation:
			job_card_doc.update({
				"workstation": self.workstation,
			})
		job_card_doc.time_logs=[]
		job_card_doc.employee = []

		if self.enable_tracking_detail:
			job_card_doc.tracking_details = []
			for row in self.tracking_details:
				job_card_doc.append("tracking_details", row)
		else:
			job_card_doc.tracking_number = self.tracking_number

		# 优先分组报工
		if self.employees:
			for row in self.employees:
				job_card_doc.append("employee", {"employee": row.employee})	
		elif self.employee:
			job_card_doc.append("employee", {"employee": self.employee})
		job_card_doc.append("time_logs", {"completed_qty": self.total_completed_qty})
		job_card_doc.for_quantity = self.total_completed_qty
		if self.enable_tracking_detail:
			job_card_doc.service_price_base_qty = self.tracking_qty
		job_card_doc.save()
		if job_card_doc.has_permission("submit"):
			job_card_doc.submit()


@frappe.whitelist()
def get_tracking_numbers(work_order, operation):
	#获取当前工序的上一个工序
	work_order_doc = frappe.get_doc("Work Order", work_order)
	prev_operation = None
	operation_in_work_order = False
	for (idx, row) in enumerate(work_order_doc.operations):
		if row.operation == operation:
			operation_in_work_order = True
			if idx:
				prev_operation = work_order_doc.operations[idx-1].operation
				break
	if not operation_in_work_order:
		frappe.throw(_("Operation {0} not in work order {1}").format(operation, work_order))
	elif not prev_operation:
		frappe.throw(_("Operation {0} in work order {1} is first operation, please go to job card directly").format(operation, work_order))
	elif prev_operation:
		# 获取前工序已报工，本工序尚未报工的追踪号
		jc_prev = frappe.qb.DocType("Job Card").as_("jc_prev")
		td_prev = frappe.qb.DocType("Tracking Detail").as_("td_prev")
		jc_cur = frappe.qb.DocType("Job Card").as_("jc_cur")
		td_cur = frappe.qb.DocType("Tracking Detail").as_("td_cur")

		sub_query = frappe.qb.from_(td_cur
		).join(jc_cur
		).on(jc_cur.name == td_cur.parent
		).where(			
			(jc_cur.work_order == work_order) &
			(jc_cur.docstatus<2) &
			(jc_cur.operation == operation) 			
		).select(td_cur.tracking_number)

		data = frappe.qb.from_(jc_prev
		).join(td_prev
		).on(jc_prev.name == td_prev.parent
		).where(
			(jc_prev.work_order == work_order) &
			(jc_prev.operation == prev_operation) &
			(jc_prev.docstatus == 1) &
			(td_prev.tracking_number.notin(sub_query))
		).select(td_prev.tracking_number
		).run(as_dict=1)

		return data
